
#ifndef __interpolatorH__
#define __interpolatorH__

#include "animation.h"
#include "dator.h"

namespace WtfEngine
{
	template <typename tType>
	struct Functions {
		struct Linear {
			static tType Evaluate(tType change, tGameTime t) {
				return change * t;
			};
			static tType GetChange(tType totChange, tGameTime totTime) {
				return totChange / totTime;
			};
		};
			
		struct Quadratic {
			static tType Evaluate(tType change, tGameTime t) {
				return change * t * t;
			};
			static tType GetChange(tType totChange, tGameTime totTime) {
				return totChange / (totTime * totTime);
			};
		};
	};
	
	template <typename tType, class Func>
	class Interpolator : public Animation {
		protected:
			typedef Interpolator<tType, Func>	tThis;
			
			tType*			mpValue;
			tType			mStart, mEnd, mChange;
			tGameTime		mTime, mTimeLimit;
			
			
		public:
			Interpolator(tType& v)
			: Animation(), mpValue(&v) {
			};
			Interpolator(tType& v, tType end, tGameTime t)
			: Animation(), mpValue(&v), mEnd(end), mTime(t), mTimeLimit(t){
			};
			
			virtual void Init() {
				Destroy();
				mTime = 0;
				mStart = *mpValue;
				mChange = Func::GetChange(mEnd - mStart, mTimeLimit);
				Animation::Init();
			};
			
			void Init(const tType& end, tGameTime t) {
				mEnd = end;
				mTimeLimit = t;
				
				Init();
			};
			
			void Init(const tType& end, tGameTime t, const Functor::tRef& rF) {
				SetOnComplete(rF);
				Init(end, t);
			};
			
			virtual void Update(tGameTime dt){
				assert(mpValue != NULL);
				mTime += dt;

				if(mTime >= mTimeLimit) {
					*mpValue = mEnd;
					this->mTime = mTimeLimit;
					Destroy();
				} else {
					*mpValue = mStart + Func::Evaluate(mChange, mTime);
				};
			};
			
			bool isRunning() const {
				return (mTime < mTimeLimit);
			};

			inline const tType& getEnd() const {
				return mEnd;
			};

			/// Sets the value pointer to NULL, should be called for safety when
			/// target object is deleted.
			void Invalidate() {
				mpValue = NULL;
			};
			
			GC_AUTOSIZE(tThis);
	};

	
	template <class tType, class Func>
	struct Interpolated : public Dator<tType> {
		typedef Interpolated<tType, Func>	tThis;
		typedef Interpolator<tType, Func>	tIpl;
		
		tGameTime			Time;
		
		typename tIpl::tRef	rInterpolator;
		
		explicit inline Interpolated(): Time(0.25) {
		};

		inline Interpolated(const tType& v, tGameTime t = 0.25)
			: Dator<tType>(v), Time(t), rInterpolator() {
		};
		
		inline ~Interpolated(){
			if(!rInterpolator.isNull()) {
				rInterpolator->Invalidate();
				if(rInterpolator->isRunning())
					rInterpolator->Destroy();
			};
		};
		
		virtual void Init(const tType& v) {
			if(rInterpolator.isNull())
				rInterpolator = new tIpl(this->Value);
			
			rInterpolator->Init(v, Time);
		};
		
		inline void Init(const tType& v, const Functor::tRef& f) {
			Init(v);
			rInterpolator->SetOnComplete(f);
		};
		
		/// Assignment
		inline void operator = (const tThis& v){
			Init(v.Value);
		};
		inline void operator = (const tType& v) {
			Init(v);
		};
	};
};
namespace Meta {
	/// Adaptor to fetch values from Interpolated
#	define META_INTERPOLATED_TYPE WtfEngine::Interpolated<T, Func>
	template <class T, class Func>
	META_VALUE_DEFAULT(META_INTERPOLATED_TYPE, WtfEngine::Dator<T>);
};

#endif
